package com.ellfors.common.http.utils

import com.ellfors.common.ext.jsonToObj
import com.ellfors.common.constant.HttpConstants
import com.ellfors.common.http.entity.BaseException
import com.ellfors.common.http.entity.BaseResponse
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import retrofit2.HttpException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.util.concurrent.TimeoutException

/**
 * 协程管理类
 */
object CoroutinesManager {

    private const val ERROR_UNKNOWN = "未知错误"
    private const val ERROR_TIMEOUT = "请求超时"
    private const val ERROR_NET = "网络错误"

    /**
     * 异步请求，自己订阅回调
     */
    fun <T> launch(
        mScope: CoroutineScope,
        block: suspend CoroutineScope.() -> BaseResponse<T>
    ): CoroutinesConverter<T> {
        val options = CoroutinesOptions<T>(mScope)
        val converter = CoroutinesConverter(options)
        mScope.launch {
            handleException({
                withContext(Dispatchers.IO) {
                    options.triggerStart()
                    block()
                }
            }, { res ->
                excuteResponse(res, {
                    options.triggerSuccess(it)
                }, {
                    options.triggerError(it)
                })
            }, {
                options.triggerError(it)
            }, {
                options.triggerComplete()
            })
        }
        return converter
    }

    /**
     * 同步请求，需要开启协程作用域 scope.launch{ }
     *
     * 1、用于单一请求只需要处理结果，不需要处理异常，出现异常时会返回null
     * 2、或者同时请求多个接口，统一处理数据，
     *    此时需要自己调用 val value = async{ getHttResult{} }，接收处调用 value.await()
     */
    suspend fun <T> getHttResult(block: suspend () -> BaseResponse<T>): T? {
        var data: T? = null
        handleException({
            withContext(Dispatchers.IO) {
                block()
            }
        }, { res ->
            excuteResponse(res, {
                data = it
            })
        })
        return data
    }

    /**
     * 处理网络错误码
     * 502、404之类的错误码可以在这里统一处理
     */
    private suspend fun <T> handleException(
        block: suspend CoroutineScope.() -> T?,
        success: suspend CoroutineScope.(T?) -> Unit,
        error: suspend CoroutineScope.(BaseException) -> Unit = {},
        complete: suspend CoroutineScope.() -> Unit = {}
    ) {
        coroutineScope {
            try {
                success(block())
            } catch (e: Exception) {
                when (e) {
                    //未知地址
                    is UnknownHostException -> {
                        error(BaseException(HttpConstants.CODE_ERROR_NET, ERROR_NET))
                    }
                    //超时
                    is TimeoutException, is SocketTimeoutException -> {
                        error(BaseException(HttpConstants.CODE_ERROR_TIMEOUT, ERROR_TIMEOUT))
                    }
                    //网络错误
                    is HttpException -> {
                        try {
                            val errorCode = e.code()
                            when (e.code()) {
                                //例如可以全局捕获502异常
                                502 -> {
                                    error(BaseException(errorCode, ""))
                                }
                                //其他尝试获取错误信息
                                else -> {
                                    val errorStr = e.response()?.errorBody()?.string()
                                    val errorBean = errorStr.jsonToObj<BaseResponse<*>>()
                                    error(
                                        BaseException(
                                            errorBean?.errorCode ?: HttpConstants.CODE_ERROR_UNKNOW,
                                            errorBean?.errorMsg ?: ERROR_UNKNOWN
                                        )
                                    )
                                }
                            }
                        } catch (e: Exception) {
                            //获取不到就当未知错误处理
                            error(BaseException(HttpConstants.CODE_ERROR_UNKNOW, ERROR_UNKNOWN))
                        }
                    }
                    //其他，未知错误
                    else -> {
                        error(BaseException(HttpConstants.CODE_ERROR_UNKNOW, ERROR_UNKNOWN))
                    }
                }
            } finally {
                complete()
            }
        }
    }

    /**
     * 解析Response，处理自己的业务码
     */
    private suspend fun <T> excuteResponse(
        response: BaseResponse<T>?,
        success: suspend CoroutineScope.(T?) -> Unit,
        error: suspend CoroutineScope.(BaseException) -> Unit = {}
    ) {
        coroutineScope {
            try {
                when (response?.errorCode) {
                    HttpConstants.CODE_RESPONSE_SUCCESS -> {
                        success(response.data)
                    }
                    else -> {
                        error(
                            BaseException(
                                response?.errorCode ?: HttpConstants.CODE_ERROR_UNKNOW,
                                response?.errorMsg ?: ERROR_UNKNOWN,
                            )
                        )
                    }
                }
            } catch (e: Exception) {
                error(BaseException(HttpConstants.CODE_ERROR_UNKNOW, e.localizedMessage ?: ""))
            }
        }
    }
}